<?php

namespace App\Models;

use Exception;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/*点位model*/

class NavigationPoint extends BaseModel
{
    use HasFactory;

    const CREATED_AT = 'create_time';
    const UPDATED_AT = 'change_time';

    protected $table = 'navigation_point';

    protected $guarded = []; //使用模型  update 更新时，允许被全部更新，若不写，则会报错 或 没任何数据变化

    /**
     * 将svg图片转为代码
     * @param $img svg图片地址
     */
    public function getLineByImg($img)
    {
        //通过svg图片转为代码
        $uploads_path = public_path('uploads');
        $img = $uploads_path . '/' . $img;
        $svg = file_get_contents($img);
        $dom = new \DOMDocument();
        @$dom->loadHTML($svg);
        $dom->saveHTML();
        $svg = $dom->saveHTML();
        return $svg;
    }

    /**
     * 列表
     * @param page int 页码
     * @param limit int 分页大小
     * @param keywords string 搜索关键词
     */
    public function lists($keywords, $limit = 10)
    {
        $res = $this->model->where(function ($query) use ($keywords) {
            if ($keywords) {
                $query->where('name', 'like', "%$keywords%");
            }
        })
            ->where('is_del', 1)
            ->paginate($limit)
            ->orderByDesc('sort')
            ->orderBy('type')
            ->orderBy('id')
            ->toArray();
        return $res;
    }

    /**
     * 查询指定区域的点位列表
     * @param $area_id 区域id
     * @param $type 1 查所有 2查结束区域 3终点区域
     * @return $data array 返回当前区域起点以及目的地
     */
    public function getFilterListByAreaId($area_id, $type = 1)
    {
        $res = $this->select('id', 'name', 'area_id', 'type', 'line')
            ->where('area_id', $area_id)
            ->where(function ($query) use ($type) {
                if ($type == 2) {
                    $query->where('type', 1)->orWhere('type', 4);
                } elseif ($type == 3) {
                    $query->where('type', 3);
                }
            })
            ->where('is_del', 1)
            ->where('is_play', 1)
            ->orderByDesc('sort')
            ->orderBy('type')
            ->orderBy('id')
            ->get();
        $start_point = [];
        $end_point = [];
        if ($res) {
            foreach ($res as $k => $v) {
                $cur_type = $v['type'];
                $cur_line = $v['line'];
                unset($v['type'], $v['line']);
                if ($cur_type == 1 || $cur_type == 4) {
                    $start_point = $v;
                    unset($res[$k]);
                } else {
                    //如果目的地还没路线，也不展示。
                    if (!empty($cur_line)) {
                        $end_point[] = $v;
                    }
                }
            }
        }
        $data['start_point'] = $start_point;
        $data['end_point'] = $end_point;
        return $data;
    }

    /**
     * 通过点位id查找点位信息
     * @param $point_id int 点位id
     * @param $type   点位类型数组或者字符串
     */
    public function getPointDataById($point_id, $type = [])
    {
        $res = $this->select('id', 'name', 'line', 'area_id')
            ->where('is_play', 1)
            ->where('is_del', 1)
            ->where('id', $point_id)
            ->where(function ($query) use ($type) {
                if ($type) {
                    $type = !is_array($type) ? explode(',', $type) : $type;
                    $query->whereIn('type', $type);
                }
            })
            ->first();
        return $res;
    }
    /**
     * 查找不同区域的可通行路线
     * @param $start_area_id int 开始区域
     * @param $end_area_id int 目的地区域
     * @param $has_search_area_id array已经查找过的区域id，找过的区域就不重复找了
     * @param $line_data array 线路
     */
    public function getLineDataByPoint($start_area_id, $end_area_id, $build_area_data, $line_data = [])
    {
        $transition_point = $this->getTranstionPointByAreaId($start_area_id, $build_area_data);
        if (empty($transition_point)) {
            return false;
        }
        $not_transtion_point = []; //不能直达的过渡点信息
        foreach ($transition_point as $k => $v) {
            if (in_array($end_area_id, $v['transtion_area_id'])) {
                //中转点可以直达指定区域，将路径信息返回即可
                $cur_line['name'] = $v['name']; //目的地名称
                $cur_line['line'] = $v['line']; //目的地线路
                $cur_line['area_id'] = $v['area_id']; //当前区域
                $cur_line['transtion_area_id'] = $end_area_id; //过渡到的区域
                $line_data[] = $cur_line;
                return $line_data; //返回找到的路线
            } else {
                $not_transtion_point[] = $v;
            }
        }
        //没找到直达的，就只有挨个找有没能到达的。
        if (empty($not_transtion_point)) {
            return false; //没有路线
        }
        //找出所有的路-数组链接
        $data = []; //装下能走的路
        $has_search_area_id = []; //确定无法达到目的区域的区域集合，避免重复操作
        foreach ($not_transtion_point as $key => $value) {
            foreach ($value['transtion_area_id'] as $k => $v) {
                if ($end_area_id == $v) {
                    //找到最后一层了
                    $data[$start_area_id][$v] = $end_area_id;
                } else {
                    if (!in_array($v, $has_search_area_id) && $v != $value['area_id']) {
                        $cur_transition_point = $this->getTranstionPointByAreaId($v, $build_area_data);
                        if (empty($cur_transition_point)) {
                            $has_search_area_id[] = $v;
                        } else {
                            $search_res = $this->searchAreaByPointData($cur_transition_point, $v, $end_area_id, $has_search_area_id, $build_area_data);
                            if ($search_res) {
                                $data[$start_area_id][$v] = $search_res;
                            }
                        }
                    }
                }
            }
        }
        //如果有路线，找出最短的
        if ($data) {
            //获取最段区域路径
            $line_area = $this->getLineAreaByData($data);
            //根据区域关系,组装数据
            $line_area_data = explode('-', $line_area);
            $last_key = count($line_area_data) - 1;
            foreach ($line_area_data as $k => $v) {
                if ($k != $last_key) {
                    $transtion_area_id = $line_area_data[$k + 1];
                    //没到最后一个点。
                    $line = $this->where('area_id', $v)
                        ->where('is_play', 1)
                        ->whereRaw('FIND_IN_SET(?, transtion_area_id)', $transtion_area_id)
                        ->first();
                    $cur_line['name'] = $line['name']; //目的地名称
                    $cur_line['line'] = $line['line']; //目的地线路
                    $cur_line['area_id'] = $line['area_id']; //当前区域
                    $cur_line['transtion_area_id'] = $transtion_area_id; //过渡到的区域
                    $line_data[] = $cur_line;
                }
            }
        }
        return $line_data;
    }
    /*获取跨越最少区域的路径*/
    public function getLineAreaByData($data, $line = "")
    {
        if (!is_array($data)) {
            return trim($line . '-' . $data, '-');
        }
        $cur_deep = 100; //默认一个很大的值，用于对比
        $last_key = "";
        foreach ($data as $k => $v) {
            if (!is_array($v)) {
                $cur_deep = 1;
                $last_key = $k;
            } else {
                $deep = $this->array_depth($v);
                if ($deep < $cur_deep) {
                    $cur_deep = $deep;
                    $last_key = $k;
                }
            }
        }
        $line = $line . '-' . $last_key;
        return $this->getLineAreaByData($data[$last_key], $line);
    }
    /**
     * 获取指定数组的深度
     */
    public function array_depth($array)
    {
        $max_deep = 1;
        foreach ($array as $value) {
            if (is_array($value)) {
                $deep = $this->array_depth($value) + 1;
                // 递归完毕后，判断每次递归的深度是否大于当前的最大深度
                if ($deep > $max_deep) {
                    $max_deep = $deep;
                }
            }
        };
        return $max_deep;
    }
    /**
     * 通过所有过渡点，分别查询可以过渡的区域数据
     * @param $start_area_id int 开始区域
     * @param $end_area_id int 目的地区域
     * @param $has_search_area_id array已经查找过的区域id，找过的区域就不重复找了
     * @param $line_data array 线路
     */
    public function searchAreaByPointData($cur_transition_point, $second_key, $end_area_id, $has_search_area_id, $build_area_data)
    {
        $data = [];
        foreach ($cur_transition_point as $k => $v) {
            if (in_array($end_area_id, $v['transtion_area_id'])) {
                //找到最近的就不继续找了
                return $end_area_id;
            } else {
                $not_transtion_point[] = $v;
            }
        }
        if (empty($not_transtion_point)) {
            $has_search_area_id[] = $second_key;
            return $data;
        }
        //继续查询
        foreach ($not_transtion_point as $key => $value) {
            foreach ($value['transtion_area_id'] as $k => $v) {
                if (!in_array($v, $has_search_area_id)) {
                    $cur_transition_point = $this->getTranstionPointByAreaId($v, $build_area_data);
                    if (empty($cur_transition_point)) {
                        $has_search_area_id[] = $v;
                    } else {
                        $search_res = $this->searchAreaByPointData($cur_transition_point, $v, $end_area_id, $has_search_area_id, $build_area_data);
                        if ($search_res) {
                            $data[$v] = $search_res;
                        }
                    }
                }
            }
        }
        return $data;
    }


    /**
     * 查找指定区域过渡点信息
     * @param $area_id int 区域id
     * @param $build_area_data array 建筑内区域id
     */
    public function getTranstionPointByAreaId($area_id, $build_area_data)
    {

        //查询过渡点位s
        $res = $this->select('id', 'name', 'line', 'area_id', 'transtion_area_id', 'type')
            ->where('is_play', 1)
            ->where('is_del', 1)
            ->whereIn('type', [3, 4])
            //            ->whereNotNull('line')
            ->where('area_id', $area_id)
            ->get()->toArray();

        if (!$res) return []; //没有过渡点
        foreach ($res as $k => $v) {
            //排除没数据的线路
            if (empty($v['line']) && $v['type'] == 3) {
                unset($res[$k]);
                continue;
            }
            //排除区域删除或者不展示的部分
            $cur_transtion_area_data = explode(',', $v['transtion_area_id']);
            $cur_transtion_area_data = array_intersect($cur_transtion_area_data, $build_area_data);
            if (empty($cur_transtion_area_data)) {
                unset($res[$k]); //如果没有可用过渡区域的数据也不要
            } else {
                $res[$k]['transtion_area_id'] = $cur_transtion_area_data;
            }
        }
        return $res;
    }

    /**
     * 根据起点或者目的地，查找对应类型数据
     * @param $build_area_ids int 建筑内的区域id
     * @param $keywords string 关键字
     * @param $keywords_type int 搜索类型 1 起点搜索 2目的地搜索
     */
    public  function getDataByType($build_area_ids, $keywords_type, $keywords, $limit = 10)
    {
        $type = $keywords_type == 1 ? [1, 4] : [2, 3];
        $res = $this->select('id', 'name', 'area_id', 'type')
            ->where('is_play', 1)
            ->where('is_del', 1)
            ->where(function ($query) use ($build_area_ids, $keywords, $type) {
                //查询区域
                $query->whereIn('area_id', $build_area_ids);
                //查询类型
                $query->whereIn('type', $type);
                //查询关键字
                if ($keywords) {
                    $query->where('name', 'like', "%$keywords%");
                }
            })
            ->orderByDesc('sort')
            ->orderBy('id')
            ->paginate($limit)
            ->toArray();
        return $res;
    }



    /**
     * 导航线路数据
     * @param $start_area_id 起点楼层
     * @param $start_point_id 起点点位id
     * @param $end_point_id  目的地点位id
     * @param $end_area_id  目的地区域id
     */
    public function navigationLineData($start_area_id, $start_point_id, $end_area_id, $end_point_id)
    {

        $line_name = [];
        //获取起点信息
        $start_point_data = $this->getPointDataById($start_point_id, [1, 4]);
        if (!$start_point_data) {
            return '导航起点信息不存在';
        }

        //获取终点信息
        $end_point_data = $this->getPointDataById($end_point_id, [2, 3]);
        if (!$end_point_data) {
            return  '导航目的地信息不存在';
        }
        //查看终点路径是否存在
        if (empty($end_point_data['line'])) {
            return '无可用路线';
        }
        //目的地楼层路线
        $end_line_data['name'] = $end_point_data['name'];
        $end_line_data['line'] = $end_point_data['line'];
        $end_line_data['area_id'] = $start_area_id; //所属区域
        $navigationAreaModel = new NavigationArea();
        $area_res = $navigationAreaModel->where('id', $start_area_id)->select('img', 'alias_name', 'name', 'rotation_angle')->first();
        if ($area_res) {
            $end_line_data['area_img'] = $area_res['img'];
            $start_area_name = !empty($area_res['alias_name']) ? $area_res['alias_name'] : $area_res['name'];
            $end_line_data['area_name'] = $start_area_name;
            $end_line_data['rotation_angle'] = $area_res['rotation_angle'];
        }

        //结束点位区域信息
        $end_area_res = $navigationAreaModel->where('id', $end_area_id)->select('img', 'alias_name', 'name')->first();
        if ($end_area_res) {
            $end_area_name = !empty($end_area_res['alias_name']) ? $end_area_res['alias_name'] : $end_area_res['name'];
        }
        //拼接上楼层别名
        $line_name['start_point_name'] = !empty($start_area_name) ? $start_point_data['name'] . '(' . $start_area_name . ')' :  $start_point_data['name']; //导航起点
        $line_name['end_point_name'] = !empty($end_area_name) ? $end_point_data['name'] . '(' . $end_area_name . ')' :  $end_point_data['name']; //导航目的地
        $data['line_name'] = $line_name;

        $end_line_data['transtion_area_id'] = $end_point_data['area_id']; //到达区域
        //如果同一个楼层，直接展示地图信息
        if ($start_area_id == $end_area_id) {
            $data['line_data'][] = $end_line_data;
        } else {
            //枚举查找可通行路线，并找出最短路线
            $build_id = $navigationAreaModel->where('id', $start_area_id)->value('build_id');
            $build_area_data = $navigationAreaModel->where('build_id', $build_id)
                ->where('is_play', 1)
                ->where('is_del', 1)
                ->pluck('id')->toArray();
            $common_line_data = $this->getLineDataByPoint($start_area_id, $end_area_id, $build_area_data);
            if (empty($common_line_data)) {
                return '无可用路线';
            }
            $common_line_data[] = $end_point_data;
            //返回前端需要的区域地图
            $line_data = [];
            $i = 0;
            foreach ($common_line_data as $k => $v) {
                //没有路线的线路直接过滤
                if ($v['line']) {
                    $area_res = $navigationAreaModel->where('id', $v['area_id'])->select('img', 'alias_name', 'name', 'rotation_angle')->first();
                    if ($area_res) {
                        $v['area_img'] = $area_res['img'];
                        $v['area_name'] = !empty($area_res['alias_name']) ? $area_res['alias_name'] : $area_res['name'];
                    }
                    $line_data[$i] = $v;
                    $i++;
                }
            }
            $data['line_data'] = $line_data;
        }
        return $data;
    }
}
